from threading import Thread



class TopicAlreadyExistsException (Exception):


    def __init__(self,topicName):
        self.value = 'Topic %s already exists' % topicName

    def __str__(self):
        return self.value


class AlreadySubscribedException (Exception):


    def __init__(self,topicName,subscriber):
        self.value = '%s: already subscribed to %s' % (str(subscriber),
                                                       topicName)
    def __str__(self):
        return self.value


class NoSuchTopicException (Exception):


    def __init__(self,topicName):
        self.value = 'No such topic: %s' % topicName

    def __str__(self):
        return self.value


class Singleton(type):
 
    def __init__(cls, name, bases, dct):
        cls.__instance = None
        type.__init__(cls, name, bases, dct)
 
    def __call__(cls, *args, **kw):
        if cls.__instance is None:
            cls.__instance = type.__call__(cls, *args,**kw)
        return cls.__instance
    

class EventManager:
    
    __metaclass__ = Singleton

    def __init__(self):
        self.__topics = { }

    def addTopic(self,name):
        topic = Topic(name)
        if self.__topics.has_key(name):
            raise TopicAlreadyExistsException(name)
        self.__topics[name] = topic
        return topic

    def removeTopic(self,name):
        self.__topics.remove(name)

    def getTopic(self,name):
        if self.__topics.has_key(name):
           return self.__topics[name]
        else:
            raise NoSuchTopicException(name)    
    

class Topic: 

    
    def __init__(self,name):
        self.__name = name
        self.__subscribers = [ ]

    def subscribe (self,subscriber):
        if subscriber not in self.__subscribers:
            self.__subscribers.append(subscriber)
        else:
            raise AlreadySubscribedException(self.__name,
                                             subscriber)

    def unsubscribe(self,subscriber):
        try:
            self.__subscribers.remove(subscriber)
        except Exception, e:
            print 'logging error: %s' % str(e)
    
    def publish(self,*args):
        launcher = LaunchEmitters(self.__subscribers,*args)
        launcher.start()


class LaunchEmitters (Thread):
    

    def __init__ (self,subscribers,*args):
        Thread.__init__(self)
        self.__subscribers = subscribers
        self.__args = args

    def run (self):
        for s in self.__subscribers:
            e = Emitter(s,*self.__args)
            e.start()


class Emitter (Thread):

    
    def __init__ (self,subscriber,*args):
        Thread.__init__(self)
        self.__subscriber = subscriber
        self.__args = args

    def run(self):
        self.__subscriber(*self.__args)
        
    

    
        
    
